/*
Copyright (C) 2008 raohmaru <http://www.raohmaru.com>

This file is part of Raohmaru AS3 Framework.

Raohmaru AS3 Framework is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Raohmaru AS3 Framework is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with Raohmaru AS3 Framework.  If not, see <http://www.gnu.org/licenses/>.
*/

package jp.raohmaru.controls
{
import fl.video.*;

import jp.raohmaru.motion.Paprika;
import jp.raohmaru.utils.StringUtil;

import flash.display.*;
import flash.events.*;
import flash.geom.Point;
import flash.utils.*;

/**
 * Se distribuye cuando la película entra o sale en modo de pantalla completa, redimensionando la instancia de Video y el aspecto.
 * @eventType fl.video.LayoutEvent.LAYOUT
 */
[Event(name="layout", type="fl.video.LayoutEvent") ]

/**
 * La clase FLVPlayer amplía la clase Control ajustando un objeto FLVPlayback y botones de navegación.
 * Los eventos generados por el objeto FLVPlayback interno son despachados a través de FLVPlayer, por lo que se pueden registar directamente desde 
 * una instancia de FLVPlayer.
 * Nota: A veces un FLV corrupto o mal creado puede provocar un mal funcionamiento en la barra de progreso. Se aconseja también no establecer un valor superior a <code>0.7</code>
 * a la propiedad <code>FLVPlayer.bufferTime</code> del reproductor interno si se produce este error al cargar por primera vez un archivo .flv.
 * @see fl.video.VideoEvent
 * @example
<listing version="3.0">
import jp.raohmaru.controls.FLVPlayer;
import fl.video.VideoEvent;
var player :FLVPlayer = new FLVPlayer(flvplayer);
	player.autoPlay = false;
	player.width = 400;
	player.height = 250;
	player.addEventListener(VideoEvent.READY, playerEventHandler);
	player.addEventListener(VideoEvent.COMPLETE, playerEventHandler);
	player.play("http://www.helpexamples.com/flash/video/caption_video.flv");	function playerEventHandler(e :VideoEvent)
{
	trace(e);
}
</listing>
 * @author raohmaru
 * @version 1.5.2
 */
public class FLVPlayer extends Control
{
	private var _flvPlayback :FLVPlayback,
				_skin : MovieClip,
				_skin_position :Point,				_skin_orig :Point,
				_skinFixedPosition :Boolean,
				_buffer_position :Point,
				_skinAutoHide :Boolean,
				_skin_timer :Timer,
				_buffer :MovieClip,
				_index :int,
				_width :Number,
				_height :Number,
				_seekBarWidth :Number;

	private const _skin_bots :Array = ["playPauseButton", "playButton", "pauseButton", "stopButton", "backButton", "forwardButton", "muteButton", "volumeBar", "seekBar", "fullScreenButton"];
	private static var	ACTIVE_FLVP_INDEX :int = -1,
						FLVP_INDEXES :int;
	
	
	/**
	 * Devuelve una referencia al objeto FLVPlayback interno.
	 */
	public function get video() :FLVPlayback
	{
		return _flvPlayback;
	}
	
	/**
	 * Especifica un MovieClip que será el aspecto del objeto FLVPlayer (que contiene los botones de control).
	 */
	public function get skin() : MovieClip
	{
		return _skin;
	}	
	public function set skin(value : MovieClip) : void
	{
		if(_skin) _flvPlayback.removeChild(_skin);
		
		// Coloca el clip dentro de la instancia de FLVPlayback para cuando se pase a pantalla completa con FLVPlayback.fullScreenTakeOver = true no desaparezca
		_skin = _flvPlayback.addChild( value.parent.removeChild(value) ) as MovieClip;
		_skin_position = new Point(_skin.x, _skin.y);
		
		if(_skin.progress_mc) _skin.progress_mc.scaleX = 0;
		
		var bot :Sprite;
		for(var i:Number=0; i<_skin_bots.length; i++)
		{
			bot = _skin[_skin_bots[i]];
			if(bot)
			{
				_flvPlayback[_skin_bots[i]] = bot;
				if(_skin_bots[i] != "volumeBar" && _skin_bots[i] != "seekBar") _flvPlayback[_skin_bots[i]].buttonMode = true;
			}
		}
		
		if(_skin.seekBar) _seekBarWidth = _skin.seekBar.width;
	}
	
	/**
	 * Indica la coordenada <i>x</i> del objeto MovieClip que contiene el aspecto, en relación a las coordenadas locales de su contenedor.
	 */
	public function get skinX() :Number
	{
		return _skin_position.x;
	}	
	public function set skinX(value :Number) :void
	{
		_skin_position.x = value;
		_skin.x = value;
	}
	
	/**
	 * Define el ancho del MovieClip que hace de aspecto del FLVPlayer.
	 */
	public function get skinWidth() :Number
	{
		return _skin.width;
	}	
	public function set skinWidth(value :Number) :void
	{
		if(_skin && !_skinFixedPosition)
		{
			var videoOffsetX : Number = value - _skin.width,
				x :Number = 100000,
				child :DisplayObject;
			
			var i :int = _skin.numChildren;
			while(--i > -1)
			{
				child = _skin.getChildAt(i);
				if(child.name != "seekBar" && child.name != "progress_mc" && child.name != "bg_mc" && describeType(child).@name != "SeekBarHandle")
				{
					if(child.x > _skin.seekBar.x)
					{
						child.x = Math.round( child.x + videoOffsetX );
						x = Math.min(x, child.x);
					}
				}
			}
					
			_seekBarWidth = x-_skin.seekBar.x;
			_skin.seekBar.width = _seekBarWidth;
			if(_skin.progress_mc.fill_mc)
				_skin.progress_mc.fill_mc.width = _seekBarWidth;
				
			if(_skin.bg_mc)
				_skin.bg_mc.width = value;
							
			// Truco de mierda #4 para que el deslizador coja el nuevo ancho
			updateControl("seekBar");
		}
	}
	
	/**
	 * Indica la coordenada <i>y</i> del objeto MovieClip que contiene el aspecto, en relación a las coordenadas locales de su contenedor.
	 */
	public function get skinY() :Number
	{
		return _skin_position.y;
	}	
	public function set skinY(value :Number) :void
	{
		_skin_position.y = value;
		_skin.y = value;
	}

	/**
	 * @private
	 */
	override public function get width() : Number
	{
		return _width;
	}
	/**
	 * @private
	 */
	override public function set width(value : Number) : void
	{
		_width = value;
		_flvPlayback.width = value;
		if(_movie.scenario_mc) _movie.scenario_mc.width = value;
		if(_buffer)
		{
			if(_buffer.screen_mc || _buffer.loading_mc)
			{
				if(_buffer.screen_mc) _buffer.screen_mc.width = value;
				if(_buffer.loading_mc) _buffer.loading_mc.x = int(value/2);
			}
			else
				_buffer.width = value;
		}
		
		skinWidth = value;
	}
	
	/**
	 * @private
	 */
	override public function get height() : Number
	{
		return _height;
	}
	/**
	 * @private
	 */
	override public function set height(value : Number) : void
	{
		_height = value;
		_flvPlayback.height = value;
		if(_movie.scenario_mc) _movie.scenario_mc.height = value;
		if(_buffer)
		{
			if(_buffer.screen_mc || _buffer.loading_mc)
			{
				if(_buffer.screen_mc) _buffer.screen_mc.height = value;
				if(_buffer.loading_mc) _buffer.loading_mc.y = int(value/2);
			}
			else
				_buffer.height = value;
		}
		
		if(_skin && !_skinFixedPosition)
		{
			_skin.y = Math.round( _height + _skin_orig.y );
			_skin_position.y = _skin.y;
		}
	}
	
	/**
	 * Define si el MovieClip del aspecto debe moverse cuando se redimensiona la instancia de FVLPlayer a través de las propiedades <code>width</code> o <code>height</code>.
	 * @default false
	 */
	public function get skinFixedPosition() :Boolean
	{
		return _skinFixedPosition;
	}	
	public function set skinFixedPosition(value :Boolean) :void
	{
		_skinFixedPosition = value;
	}

	/**
	 * Cadena que especifica la URL del archivo FLV que se va a transmitir y cómo se debe transmitir.
	 * @see fl.video.FLVPlayback#source
	 */
	public function get source() : String
	{
		return _flvPlayback.source;
	}	
	public function set source(value : String) : void
	{
		_flvPlayback.source = value;
		if(getVideoPlayer().netStream && value != "__no__.flv")
		{
			getVideoPlayer().netStream.addEventListener(NetStatusEvent.NET_STATUS, onStateChange);
			// Establece esta instancia como activa, y toma el control en caso de fullscreen
			ACTIVE_FLVP_INDEX = _index;
			_flvPlayback.fullScreenTakeOver = true;
		}
	}
	
	/**
	 * Define si debe reproducirse automáticamente tras definir la propiedad <code>source</code>.
	 * @see fl.video.FLVPlayback#autoPlay
	 * @default true
	 */
	public function get autoPlay() :Boolean
	{
		return _flvPlayback.autoPlay;
	}	
	public function set autoPlay(value :Boolean) : void
	{
		_flvPlayback.autoPlay = value;
	}
	
	/**
	 * Valor booleano que, si es <code>true</code>, hace que el archivo FLV se rebobine hasta el fotograma 1 cuando se detenga la reproducción,
	 * ya sea porque el reproductor llegó al final del flujo o porque se llamó al método <code>stop()</code>. Esta propiedad no se utiliza para flujos dinámicos.
	 * @see fl.video.FLVPlayback#autoRewind
	 * @default true
	 */
	public function get autoRewind() :Boolean
	{
		return _flvPlayback.autoRewind;
	}	
	public function set autoRewind(value :Boolean) : void
	{
		_flvPlayback.autoRewind = value;
	}
	
	/**
	 * Especifica si el vídeo debe suavizarse (interpolarse) al ajustarlo a escala.
	 * @see flash.media.Video#smoothing
	 * @default false
	 */
	public function get smoothing() :Boolean
	{
		return getVideoPlayer().smoothing;
	}	
	public function set smoothing(value :Boolean) : void
	{
		getVideoPlayer().smoothing = value;
	}
	
	/**
	 * Esta propiedad especifica el valor múltiple máximo que se utilizará para ajustar el aspecto cuando entre en el modo de pantalla
	 * completa con un Flash Player que admita la aceleración de hardware. 
	 * @default 4
	 */
	public function get skinScaleMaximum() :Number
	{
		return _flvPlayback.skinScaleMaximum;
	}	
	public function set skinScaleMaximum(value :Number) :void
	{
		_flvPlayback.skinScaleMaximum = value;
	}

	/**
	 * Valor booleano que si es <code>true</code> oculta el aspecto del control cuando el ratón no está sobre el vídeo.
	 * @default false
	 */
	public function get skinAutoHide() :Boolean
	{
		return _skinAutoHide;
	}	
	public function set skinAutoHide(value :Boolean) : void
	{
		_skinAutoHide = value;
		
		if(!_skin) return;
		
		if(value)
		{
			if(!_skin_timer)
			{
				_skin_timer = new Timer(5000, 1);
				_skin_timer.start();
				_skin_timer.addEventListener(TimerEvent.TIMER_COMPLETE, skinTimerHandler);
				_movie.addEventListener(MouseEvent.MOUSE_MOVE, skinTimerHandler);
				// Cuando se cambia a Pantalla completa el FLVPlayback se superpone a todo y sólo él recibe eventos				_flvPlayback.addEventListener(MouseEvent.MOUSE_MOVE, skinTimerHandler);
			}
		}
		else
		{
			_skin_timer.stop();
			_skin_timer.removeEventListener(TimerEvent.TIMER_COMPLETE, skinTimerHandler);
			_skin_timer = null;
			_movie.removeEventListener(MouseEvent.MOUSE_MOVE, skinTimerHandler);			_flvPlayback.removeEventListener(MouseEvent.MOUSE_MOVE, skinTimerHandler);
		}
	}
	
	/**
	 * Tiempo de espera en milisegundos para que el aspecto se oculte automáticamente.
	 * Debe especificarse después de establecer a <code>skinAutoHide = true</code>.
	 * @default 5000 milisegundos (5 segundos).
	 * @see #skinAutoHide()
	 */
	public function get skinHideTime() :Number
	{
		return _skin_timer.delay;
	}	
	public function set skinHideTime(value :Number) :void
	{
		if(_skin_timer)
		{
			_skin_timer.reset();
			_skin_timer.delay = value;
			_skin_timer.start();
		}
	}
	
	/**
	 * Valor que especifica el número de segundos que se almacenarán en la memoria antes de que se inicie la reproducción de un flujo de vídeo.
	 * @default 0.1 segundos.
	 * @see fl.video.FLVPlayback#bufferTime
	 */
	public function get bufferTime() :Number
	{
		return _flvPlayback.bufferTime;
	}	
	public function set bufferTime(value :Number) :void
	{
		_flvPlayback.bufferTime = value;
		 //Truco de mierda #3 para que la barra de progreso no desaparezca con valores superiores a 0.7
		 if(value > .7)
			var progress_mc :MovieClip = new SeekBar();
	}
	
	/**
	 * Devuelve una referencia al clip de película que actua como buffer de vídeo.
	 */
	public function get buffer() :MovieClip
	{
		return _buffer;
	}
	
	
	
	/**
	 * Crea un nuevo objeto FLVPlayer y establece las propiedades del objeto FLVPlayback interno <code>bufferTime</code> a <code>.5</code>
	 * y <code>autoRewind</code> a <code>true</code>.
	 * @param movie Objeto MovieClip que es la representación gráfica del control
	 */
	public function FLVPlayer(movie :MovieClip)
	{
		super(movie);
	}
	
	/**
	 * @private
	 */
	override protected function init() :void
	{
		_flvPlayback = _movie.video;
		_flvPlayback.autoRewind = true;
		_flvPlayback.playheadUpdateInterval = 100;
		
		// Desactiva que tome el control al fullscreen por si hay más de una instancia de FLVPlayer en la película (el último en entrar gana el control)
		_flvPlayback.fullScreenTakeOver = false;
		_index = FLVP_INDEXES++;
		
		_flvPlayback.addEventListener(VideoEvent.PLAYHEAD_UPDATE, update);
		_flvPlayback.addEventListener(MetadataEvent.METADATA_RECEIVED, onMetadata);
		_flvPlayback.addEventListener(VideoEvent.STATE_CHANGE, onStateChange);
		_flvPlayback.addEventListener(VideoEvent.BUFFERING_STATE_ENTERED, onStateChange);
		
		_width = _flvPlayback.width;		_height = _flvPlayback.height;
		
		if(_movie.buttons)
		{
			skin = _movie.buttons;
			_skin_orig = new Point(skin.x, skin.y - _height);
		}
		
		if(_movie.buffer_mc)
		{
			// Coloca el clip dentro de la instancia de FLVPlayback para cuando se pase a pantalla completa con FLVPlayback.fullScreenTakeOver = true no desaparezca
			_buffer = _flvPlayback.addChild( _movie.removeChild(_movie.buffer_mc) ) as MovieClip;
			
			_buffer_position = new Point(_buffer.x, _buffer.y);
			_buffer.alpha = 1;
			_buffer.visible = false;
		}
		
		// Registramos todos los eventos de FLVPlayback para despacharlos a través de FLVPlayer
		// ¿Necesario? No estoy muy seguro...
		_flvPlayback.addEventListener(VideoEvent.AUTO_REWOUND, eventHandler);
		_flvPlayback.addEventListener(VideoEvent.CLOSE, eventHandler);
		_flvPlayback.addEventListener(VideoEvent.COMPLETE, eventHandler);
		_flvPlayback.addEventListener(MetadataEvent.CUE_POINT, eventHandler);
		_flvPlayback.addEventListener(VideoEvent.FAST_FORWARD, eventHandler);
		_flvPlayback.addEventListener(VideoEvent.PAUSED_STATE_ENTERED, eventHandler);
		_flvPlayback.addEventListener(VideoEvent.PLAYING_STATE_ENTERED, eventHandler);
		_flvPlayback.addEventListener(VideoProgressEvent.PROGRESS, eventHandler);
		_flvPlayback.addEventListener(VideoEvent.READY, eventHandler);
		//_flvPlayback.addEventListener(LayoutEvent.LAYOUT, eventHandler);
		_flvPlayback.addEventListener(VideoEvent.REWIND, eventHandler);
		_flvPlayback.addEventListener(VideoEvent.SCRUB_FINISH, eventHandler);
		_flvPlayback.addEventListener(VideoEvent.SCRUB_START, eventHandler);
		_flvPlayback.addEventListener(VideoEvent.SEEKED, eventHandler);
		_flvPlayback.addEventListener(SkinErrorEvent.SKIN_ERROR, eventHandler);
		_flvPlayback.addEventListener(VideoEvent.SKIN_LOADED, eventHandler);
		_flvPlayback.addEventListener(VideoEvent.STOPPED_STATE_ENTERED, eventHandler);
		_flvPlayback.addEventListener(SoundEvent.SOUND_UPDATE, eventHandler);
		
		_movie.stage.addEventListener(FullScreenEvent.FULL_SCREEN, fullScreenHandler);
	}
	
	/**
	 * Reproduce el flujo de vídeo. Sin parámetros, el método sólo pasa el FLV del estado de pausa (PAUSED) o el estado detenido (STOPPED)
	 * al estado de reproducción (PLAYING).<br>
	 * Si hay multiples instancias de FLVPlayer en la película, la propiedad <code>FLVPlayback.fullScreenTakeOver</code> de la instancia de FLVPlayer que se está
	 * reproduciendo se establece a <code>true</code> para que tome el control a pantalla completa. Cuando quiera cambiar de instancia de FLVPlayback,
	 * cierre el flujo de vídeo actual con <code>FLVPlayer.close()</code> para que deje de ser el objeto activo.
	 * @param source Cadena que especifica la URL del archivo FLV
	 * @param totalTime Número que representa el tiempo total de reproducción del vídeo
	 * @param isLive Indica si el flujo de vídeo es dinámico
	 * @see fl.video.FLVPlayback#play()
	 */
	public function play(source :String = null, totalTime :Number = NaN, isLive :Boolean = false) :void
	{
		_flvPlayback.play( source, totalTime, isLive);
		if(source) getVideoPlayer().netStream.addEventListener(NetStatusEvent.NET_STATUS, onStateChange);
		enabled = true;
		// Establece esta instancia como activa, y toma el control en caso de fullscreen
		ACTIVE_FLVP_INDEX = _index;
		_flvPlayback.fullScreenTakeOver = true;
	}

	/**
	 * Detiene la reproducción del vídeo. Si el valor de la propiedad <code>autoRewind</code> es <code>true</code>, el archivo FLV se rebobina hasta el principio.
	 * @see fl.video.FLVPlayback#stop()
	 */
	public function stop() :void
	{
		_flvPlayback.stop();
	}
	
	/**
	 * Rebobina la reproducción hasta el segundo 0.
	 */
	public function rewind() :void
	{
		_flvPlayback.seek(0);
	}
	
	/**
	 * Pausa la reproducción del flujo de vídeo.
	 * @see fl.video.FLVPlayback#close()
	 */
	public function pause() :void
	{
		_flvPlayback.pause();
	}

	/**
	 * Fuerza el cierre del flujo de vídeo y desactiva el control. Para volver a utilizar el FLVPlayer, se ha de llamar al método <code>play()</code>
	 * indicando un archivo FLV.
	 * También establece la propiedad <code>FLVPlayback.fullScreenTakeOver</code> a <code>false</code>, para que en el caso de haber más instancias de
	 * FLVPlayer en el escenario éste no tome el control al cambiar a pantalla completa.
	 * <br><strike><b>Nota:</b> Debido a que se utiliza un truco para cerrar el flujo (ver código), en la consola de salida aparecerá un error
	 * ("Error al abrir la URL").</strike>
	 * @see fl.video.VideoPlayer#close()
	 * @see Control#enabled
	 */
	public function close() : void
	{
		var vp :VideoPlayer = getVideoPlayer();
		if(vp.netStream)
			vp.netStream.removeEventListener(NetStatusEvent.NET_STATUS, onStateChange);
			vp.close();
			
		// Truco de mierda para poder reproducir de nuevo el mismo FLV, si no peta ya que intenta reconectar
		source = "__no__.flv";
		
		enabled = false;
		
		// Desactiva la instancia para que no tenga el control
		ACTIVE_FLVP_INDEX = -1;
		_flvPlayback.fullScreenTakeOver = false;
	}

	/**
	 * Obtiene el reproductor de vídeo interno especificado por el valor de <code>FLVPlayback.activeVideoPlayerIndex</code>. 
	 */
	public function getVideoPlayer() :VideoPlayer
	{
		return _flvPlayback.getVideoPlayer(_flvPlayback.activeVideoPlayerIndex);
	}

	
	
	private function onMetadata(e :MetadataEvent) :void
	{
		if(_skin && _skin.total_time) _skin.total_time.text = $number2Time(_flvPlayback.metadata.duration);
		
		dispatchEvent(e);
	}
	
	private function update(e :VideoEvent) :void
	{
		if(_skin && _skin.progress_mc) _skin.progress_mc.scaleX = _flvPlayback.playheadPercentage/100;
		if(_skin && _skin.current_time) _skin.current_time.text = $number2Time(_flvPlayback.playheadTime);
		
		dispatchEvent(e);
	}

	private function onStateChange(e :Event) :void
	{
		if(e is VideoEvent)
		{
			var state :String = VideoEvent(e).state;
			if(e.type == VideoEvent.BUFFERING_STATE_ENTERED || state == VideoState.BUFFERING || state == VideoState.LOADING)
			{
				if(_buffer) _buffer.visible = true;
			}
			else
			{
				if(VideoEvent(e).state == VideoState.PAUSED)
				{
					//_flvPlayback.play();
					//trace("play");
				}
			}
			//trace(state);
		}
		else if(e is NetStatusEvent)
		{
			if(_buffer)
			{
				var code :String = NetStatusEvent(e).info.code;
				if(code == "NetStream.Buffer.Empty") _buffer.visible = true;				else if(code == "NetStream.Buffer.Full" || code == "NetStream.Play.Stop") _buffer.visible = false;
			}
		}
		
		dispatchEvent(e);
	}
	
	private function eventHandler(e :Event) :void
	{
		dispatchEvent(e);
	}
	
	private function fullScreenHandler(e :FullScreenEvent) :void
	{
		// Sólo el FLVPlayer activo tiene el control en pantalla completa
		if(ACTIVE_FLVP_INDEX != _index) return;
		
		var isFS :Boolean = (_movie.stage.displayState == StageDisplayState.FULL_SCREEN);
		
		if(skin)
		{
			_skin.x = (isFS) ? int(_movie.stage.stageWidth/2-_skin.width/2) : _skin_position.x;
			_skin.y = (isFS) ? int(_movie.stage.stageHeight-_skin.height) : _skin_position.y;
			
			// Truco de mierda #2 para que el deslizador de la barra de progreso y la barra de volumen no desaparezcan al cambiar a pantalla completa.
			// Es un bug documentado en Flash CS3.
			updateControl("seekBar");			updateControl("volumeBar");
		}		
		
		if(_buffer)
		{
			_buffer.x = (isFS) ? 0 : _buffer_position.x;
			_buffer.y = (isFS) ? 0 : _buffer_position.y;
			
			if(_buffer.screen_mc || _buffer.loading_mc)
			{
				if(_buffer.screen_mc)
				{
					_buffer.screen_mc.width = (isFS) ? _movie.stage.stageWidth : _width;					_buffer.screen_mc.height = (isFS) ? _movie.stage.stageHeight : _height;
				}
				if(_buffer.loading_mc)
				{
					_buffer.loading_mc.x = int((isFS ? _movie.stage.stageWidth : _width)/2);					_buffer.loading_mc.y = int((isFS ? _movie.stage.stageHeight : _height)/2);				}
			}
			else
			{
				_buffer.width = (isFS) ? _movie.stage.stageWidth : _width;
				_buffer.height = (isFS) ? _movie.stage.stageHeight : _height;
			}
		}
		
		dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT));
	}

	private function skinTimerHandler(e :Event) :void
	{
		if(e.type == TimerEvent.TIMER_COMPLETE)
		{
			Paprika.add(_skin, .2, {autoAlpha:0});
		}
		else
		{
			Paprika.add(_skin, .2, {autoAlpha:1});
			_skin_timer.reset();			_skin_timer.start();
		}
	}


	private function updateControl(control :String) :void
	{
		var	old_bot :DisplayObject = _skin.getChildByName(control);
		if(old_bot)
		{
			var ClassReference:Class = getDefinitionByName( control.charAt(0).toUpperCase() + control.substr(1)) as Class,
				className :String;
	
			var bot :MovieClip = new ClassReference();
				bot.name = control;
				bot.x = old_bot.x;
				bot.y = old_bot.y;
			if(control == "seekBar")
				bot.width = _seekBarWidth;
			
			_flvPlayback[control] = null;
			var i :int = _skin.getChildIndex(old_bot);
			_skin.removeChild(old_bot);
			_skin.addChildAt(bot, i);
			
			if(control == "seekBar" || control == "volumeBar")
			{
				i = _skin.numChildren;
				while(--i > -1)
				{
					className = describeType(_skin.getChildAt(i)).@name;
					if(className == "SeekBarHandle" && control == "seekBar" || className == "VolumeBarHandle" && control == "volumeBar")
					{
						_skin.removeChildAt(i);
					}
				}
			}
		
			Paprika.wait(this, .1, 0, function() :void
			{
				_flvPlayback[control] = bot;
			});
		}
	}
	
	private function $number2Time(num :Number) :String
	{
		var min :Number = Math.floor(num/60),
			seg :Number = Math.ceil(num) - 60*min;
			
		return StringUtil.toDigits(min, 2) + ":" + StringUtil.toDigits(seg, 2);
	}
}
}